import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;


public class DAIRS
{

	ArrayList<Server> physicalServers;
	ArrayList<Server> IBLphysicalServers;
	
	Queue<Task> waitingQueue;
	Queue<Task> requestingQueue;
	Queue<Task> optimizingQueue;
	Queue<Task> deletingQueue;
	
	Equations equations;
	
	int time;
	
	public DAIRS()
	{
		
		physicalServers = new ArrayList<Server>();
		IBLphysicalServers = new ArrayList<Server>();
		
		waitingQueue = new LinkedList<Task>();
		requestingQueue = new LinkedList<Task>();
		optimizingQueue = new LinkedList<Task>();
		deletingQueue = new LinkedList<Task>();
		
		equations = new Equations();
		
		time = 0;
		
	}
	
	
	public void startAlgorithm()
	{
		time = 0;
		
		while(!allQueuesEmpty()){
			step1();
			time++;
		}
		
	}


	/**
	 * calculates the IBL for all the servers.
	 */
	public void step1()
	{
		this.randomizeAll();
		equations.IBLEquation(IBLphysicalServers);
		step3();
	}

	
	/**
	 * Handling the tasks in the waiting queue.
	 */
	public void step3()
	{
		if(waitingQueue.isEmpty()){
			step4();
			return;
		}
		
		Queue<Task> temp = new LinkedList<Task>();
		
		while(!waitingQueue.isEmpty()){
			
			Task t = waitingQueue.poll();
			boolean success = step7(t);
			
			if(success)
				deletingQueue.offer(t);
			else
				temp.offer(t);
			
		}
		
		while(!temp.isEmpty())
			waitingQueue.offer(temp.poll());
		
	}
	
	
	/**
	 * Handling the tasks in the requesting queue.
	 */
	public void step4()
	{
		Queue<Task> temp = new LinkedList<Task>();
		
		while(!requestingQueue.isEmpty()){
			
			if(requestingQueue.peek().startTime <= time){
				
				Task t = requestingQueue.poll();
				boolean success = step7(t);
				if(success)
					deletingQueue.offer(t);
				else
					waitingQueue.offer(t);
				
			}else
				temp.offer(requestingQueue.poll());
			
		}
		
		while(!temp.isEmpty())
			requestingQueue.offer(temp.poll());
		
		step5();

	}
	
	
	/**
	 * Handling the tasks in the optimizing queue.
	 */
	public void step5()
	{
		if(!optimizingQueue.isEmpty()){
			
			step10();
			
		}
		
		step6();

	}
	
	
	/**
	 * Handling the tasks in the deleting queue.
	 */
	public void step6()
	{
		Queue<Task> temp = new LinkedList<Task>();
		
		while(!deletingQueue.isEmpty()){
			
			Task t = deletingQueue.poll();
			
			if((t.endTime + t.delayTime) <= time)
				t.vm.PhysicalServer.VMs.remove(t.vm);
			else
				temp.offer(t);
							
		}
		
		while(!temp.isEmpty())
			deletingQueue.offer(temp.poll());
		
	}
	
	
	/**
	 * The allocation algorithm.
	 * 
	 * @param t the task to be added.
	 * @return "true" if allocation is successful, "false" if not.
	 */
	public boolean step7(Task t)
	{

		sort(IBLphysicalServers);
		
		boolean allocationSuccessful = false;
		
		for(Server s : physicalServers)
			if(s.canBeAdded(t.vm)){
				s.addVM(t.vm);
				t.delayTime = time - t.startTime;
				allocationSuccessful = true;
				break;
			}

		if(IBLphysicalServers.get(0).canBeAdded(t.vm)){
			IBLphysicalServers.get(0).addVM(new VirtualMachine(t.vm.Name, t.vm.CPU, t.vm.Mem, t.vm.BW));
			t.delayTime = time - t.startTime;
			allocationSuccessful &= true;
		}
		
		equations.IBLEquation(IBLphysicalServers);
		
		return allocationSuccessful;
	}
	
	
	/**
	 * Add task t to the waiting queue.
	 * 
	 * @param t the task to be added to the a=waiting queue.
	 */
	public void step9(Task t)
	{
		waitingQueue.offer(t);
	}
	
	
	public boolean step10()
	{
		return true;
	}
	
	
	/**
	 * sorts the servers inside the arraylist ascending according to their utilization values.
	 * @param list
	 */
	public void sort(ArrayList<Server> list){
		
		ArrayList<Server> temp = new ArrayList<Server>();
		for(int i = 0; i < list.size(); i++){
			
			Server x = list.get(i);
			
			if(temp.size() == 0){
				temp.add(x);
				continue;
			}
			
			boolean flag = false;
			
			for(int j = 0; j < temp.size(); j++)
				if(temp.get(j).utilization > x.utilization){
					temp.add(j, x);
					flag = true;
					break;
				}
			if(!flag)
				temp.add(x);
			
		}
		
		for(int i = 0; i < list.size(); i++)
			list.set(i, temp.get(i));
		
	}
	
	
	/**
	 * generates new random values for the usage of each virtual machine on all the servers.
	 */
	public void randomizeAll(){
		
		for(Server s : physicalServers)
			s.randomizeUsage();
		for(Server s : IBLphysicalServers)
			s.randomizeUsage();
		
	}
	
	
	/**
	 * adds a physical server to cloud.
	 * 
	 * @param s the server to be added.
	 */
	public void addPhysicalServer(Server s){
		
		physicalServers.add(new Server(s.Name, s.CPU, s.Mem, s.BW));
		IBLphysicalServers.add(new Server(s.Name, s.CPU, s.Mem, s.BW));
		
	}
	
	
	/**
	 * Checks if all of the four queues are empty.
	 * @return "true" if all queues are empty, "false" otherwise.
	 */
	public boolean allQueuesEmpty() {
		return (waitingQueue.isEmpty() && requestingQueue.isEmpty() && optimizingQueue.isEmpty() && deletingQueue.isEmpty());
	}
	
	
}